#include "../bulk_storage/bulk_storage.h"

//#include "mTree.h"

#include "mTreeNode.h"

#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <values.h>
struct mTree * mTree_new_memory(size_t objectSize, double (*distance)(const void*,const void*)) {
  struct mTree * tree = (struct mTree *) malloc(sizeof(struct mTree));

  tree->oStore = store_open_memory(objectSize, 100);
  tree->nStore = store_open_memory(mTreeNode_sizeof(), 100);


  //  tree->oStore = ms_open(objectSize, 100);
  //  tree->nStore = ms_open(mTreeNode_sizeof(), 100);
  //  tree->rootNode = ISNULL;
  //  tree->rootNode = mTreeNode_new(tree, ISNULL, 0, ISNULL);
  tree->distance = distance;
  return tree;

}
/* Old way
struct mTree * mTree_new_cached(const char * oFile, const char * oFat,
				const char * nFile, const char * nFat,
				size_t objectSize, size_t cacheSize,
				double (*distance)(void*,void*)) {
  struct mTree * tree = (struct mTree *) malloc(sizeof(struct mTree));
  struct mTreeNode * oldRoot;
  tree->oStore = store_open_cached(oFile, oFat, objectSize, 100, cacheSize);
  tree->nStore = store_open_cached(nFile, nFat, mTreeNode_sizeof(), 100, cacheSize);

  oldRoot = store_read(tree->nStore, 0);
  if(oldRoot->parent != ISNULL) { // ISNULL => tree already existed on disk.
    store_release(tree->nStore, 0);  //New file
    tree->rootNode = mTreeNode_new(tree, ISNULL, 0);
    assert (tree->rootNode == 0);
  } else {
    tree->rootNode = oldRoot->dbObject;
    oldRoot->dbObject = ISNULL;
    store_write(tree->nStore, 0);
    store_release(tree->nStore, 0);  // Existing file.
  }
  tree->distance = distance;
  return tree;

}
*/

struct mTree * mTree_open_cached(struct store * oStore, const char * nFile,
				const char * nFat, size_t cacheSize,
				double (*distance)(const void*,const void*)) {
  struct mTree * tree = (struct mTree *) malloc(sizeof(struct mTree));
  struct mTreeNode * oldRoot;
  int i;

  tree->oStore = oStore;
  tree->nStore = store_open_cached(nFile, nFat, mTreeNode_sizeof(), 100, cacheSize);

  tree->rootNode = 0;
  oldRoot = store_read(tree->nStore, 0);
  if(oldRoot->parent != ISNULL) { // ISNULL => tree already existed on disk.

    // New file

    off_t newRoot = store_new(tree->nStore);
    assert(newRoot == 0);

    oldRoot->parent = ISNULL;
    for(i = 0; i < MTREE_BFACTOR; i++) {
      oldRoot->children[i] = ISNULL;
    }
    oldRoot->radius = MAXDOUBLE;
    oldRoot->pDistance = MAXDOUBLE;
    oldRoot->dbObject = ISNULL;
    oldRoot->isLeaf = 1;
    store_write(tree->nStore, 0);

  } else {

    // Existing file.  Make sure the children agree that this is the root.
    if(!oldRoot->isLeaf) {
      for(i = 0; i < MTREE_BFACTOR; i++) {
	if(oldRoot->children[i] != ISNULL) {
	  struct mTreeNode * child = store_read(tree->nStore, oldRoot->children[i]);
	  assert((child->parent == 0) &&
		 (child->children[0] != 0));  // check that the child's children[0] isn't zero.  This ensures that child has been initialized.
	  store_release(tree->nStore, oldRoot->children[i]);
	}
      }
    }

  }

  assert(oldRoot->parent == ISNULL);
  assert(oldRoot->dbObject == ISNULL);

  store_release(tree->nStore, 0);

  tree->distance = distance;


  return tree;


}

void mTree_close(struct mTree * tree) {
  struct mTreeNode * zero = store_read(tree->nStore, 0);

  assert((!zero)||zero->parent == ISNULL);
  //  assert(zero->dbObject == ISNULL); // Removed this check so crashed out read only trees are still valid...

  //  zero->dbObject = tree->rootNode;

  store_write(tree->nStore, 0);
  store_release(tree->nStore, 0);
  if(tree->oStore->functions->close == (void (*)(void*))&ms_close) {
    store_close(tree->oStore);
  } // Otherwise, it's the user's responsibility.

  store_close(tree->nStore);
  free(tree);
}

void mTree_insert(struct mTree * tree, off_t object /*void * object*/) {

  void * object_ptr = store_read(tree->oStore, object);
  struct mTreeNode * rootNode_ptr = store_read(tree->nStore, tree->rootNode);

  mTreeNode_insert(tree, tree->rootNode, rootNode_ptr, object, object_ptr, 0.0);

  store_release(tree->oStore, object);
  store_release(tree->nStore, tree->rootNode);

}


/* To do this properly, we'll need a struct capable of holding the
   data returned by the query.  It should have an iterator-style
   interface--That's a bit of design work in of itself, so don't
   bother for now.--Instead, just print out the hits as they're encountered.*/

void mTree_rangeQuery(struct mTree * tree, void * queryObject, int queryDist,
		      void (*processHit)(void *, void *), void * state) {
  const struct mTreeNode * root_ptr = store_read(tree->nStore, tree->rootNode);
  mTreeNode_query(tree, root_ptr, queryObject, queryDist * 1.0001 , processHit, state); // 1.0001 takes floating point error into account...
  store_release(tree->nStore, tree->rootNode);
}


void mTree_print(struct mTree * tree) {
  //  struct mTree* root;
  printf("Root\n");

  mTreeNode_print(tree, tree->rootNode, 0);

  //  printf("Done\n");
}

off_t mTree_size(struct mTree * tree) {
  struct mTreeNode * root_ptr = store_read(tree->nStore, tree->rootNode);
  off_t ret = mTreeNode_size(tree, root_ptr);
  store_release(tree->nStore, tree->rootNode);
  return ret;
}

void mTree_verify(struct mTree * tree) {
  struct mTreeNode * root_ptr = store_read(tree->nStore, tree->rootNode);
  assert(tree->rootNode == 0);
  mTreeNode_verify(tree, tree->rootNode, root_ptr);
  store_release(tree->nStore, tree->rootNode);
}

void mTree_printStats(struct mTree * tree) {
  printf("oStore: ");
  cs_print(tree->oStore->store_p);
  printf("nStore: ");
  cs_print(tree->nStore->store_p);
}

/*double xyzi_dist(struct xyzi * a, struct xyzi * b) {
  double ret;

  ret =
    (double)(a->x - b->x) * (double)(a->x - b->x) +
    (double)(a->y - b->y) * (double)(a->y - b->y) +
    (double)(a->z - b->z) * (double)(a->z - b->z);
  return sqrt(ret);
}


void xyzi_print(struct xyzi * a, void * null) {
  printf("(%d:\t%d,\t%d,\t%d)\n", a->i, a->x, a->y, a->z);
  }*/
